import Environment, Grid, Main, pygame, random #@UnresolvedImport

class Nanobot(pygame.sprite.Sprite):
    ''' The nanobot class. Currently very empty.
        Will soon have many more methods for interacting
        with the the environment and other nanobots.
    '''
    # Initializes the nanobot, defining it's sprite, vision radius, starting location
    def __init__(self, loc, gridsize, flag = 0, visionRadius = 3):
        pygame.sprite.Sprite.__init__(self)
        self.visionRadius = visionRadius
        self.color = (0, 0, 255)
        self.board = None
        self.image = pygame.Surface([gridsize,gridsize])
        self.image.fill(self.color)
        self.rect = self.image.get_rect()
        self.loc = loc
        self.rect.topleft = [loc[0]*10, loc[1]*10]
        self.flag = flag
        
    # Similar to a toString() method in java
    def __repr__(self):
        return "Nanobot"

    # Updates the internal representation of the grid such that all
    # squares currently in view of the bot are marked as seen.
    # If one of these squares is a that does not already have a bot
    # on it, the nanobot will mark that as a target goal.
    def nanoVision(self):
        startRow = self.loc[0] - self.visionRadius
        startCol = self.loc[1] - self.visionRadius
        if startRow < 0:
            startRow = 0
        if startCol < 0:
            startCol = 0
        goalLocs = []
        row = startRow
        col = startCol
        while row < self.loc[0] + self.visionRadius and row < self.board.size:
            col = startCol
            while col < self.loc[1] + self.visionRadius and col < self.board.size:
                square = self.board.gridrepr[row][col]
                square.viewed = True
                if square.contains(Environment.Goal) and not square.contains(Nanobot):
                    goalLocs.append(square.loc)
                col += 1
            row += 1
        return goalLocs
        
    # Returns a now (y,x) coordinate that is one square towards loc from
    # the bots current location.
    def moveTo(self, loc):
        newX = newY = 0
        if self.loc[0] < loc[0]:
            newY = 1
        elif self.loc[0] > loc[0]:
            newY = -1
        if self.loc[1] < loc[1]:
            newX = 1
        elif self.loc[1] > loc[1]:
            newX = -1
        return (self.loc[0] + newY, self.loc[1] + newX)
    
    # Checks to see if the current location of the bot is a goal state.
    def goalCheck(self):
        for item in self.board.gridrepr[self.loc[0]][self.loc[1]].contents:
            if isinstance(item,Environment.Goal):
                return True
        return False
    
    # Chooses what the next move of the bot should be. If the nanobot is
    # on a goal square, it does nothing. If the nanobot is not on a goal, 
    # but knows of one that is unoccupied, it will move towards it.
    # Otherwise, it moves towards squares whose contents are unknown.
    def performAction(self):
        if self.goalCheck():
            return
        goalLocs = self.nanoVision()
        if goalLocs:
            (newY, newX) = self.moveTo(goalLocs[0])
        else:    
            if self.flag == 0:
                (newY, newX) = self.nanoSearch()
            if self.flag == 1:
                (newY, newX) = self.nanoRand()
            
        try:
            self.board.gridrepr[self.loc[0]][self.loc[1]].contents.remove(self)
        except ValueError:
            pass
        self.loc[0] = newY
        self.loc[1] = newX
        self.rect.topleft = [self.loc[0]*10, self.loc[1]*10]
        self.board.gridrepr[self.loc[0]][self.loc[1]].contents.append(self)
    
    # Returns a randomly choosen square adjactent to the bot's
    # current location.
    def nanoRand(self):
        while 1:
            deltaX = random.randint(-1,1)
            deltaY = random.randint(-1,1)
            (test, newY, newX) = self.validMove(deltaY,deltaX)
            if test == True:
                return (newY, newX)
    
    
    def validMove(self, deltaY, deltaX):
        newX = self.loc[1]
        newY = self.loc[0]
        if 0 <= self.loc[1] + deltaX < self.board.size:
            newX += deltaX
        if 0 <= self.loc[0] + deltaY < self.board.size:
            newY += deltaY
        if self.board.gridrepr[newY][newX].contains(Nanobot):
                return (False, newY, newX)
        return (True, newY, newX)
    
    def unknownCheck(self, rowcol):
        unknowns = 0
        for i in range(-self.visionRadius, self.visionRadius +1):
            testY = rowcol[0] + i 
            if not 0 <= testY < self.board.size:
                testY = rowcol[0]
            for j in range(-self.visionRadius, self.visionRadius +1):
                testX = rowcol[1] + j
                if not 0 <= testX < self.board.size:
                    testX = rowcol[1]
                if not self.board.gridrepr[testY][testX].viewed:
                    unknowns += 1 
        return unknowns
    
    def nanoSearch(self):
        maxUnknowns = 0
        bestmove = []
        for i in [-1,0,1]:
            for j in [-1,0,1]:
                (test, testY, testX) = self.validMove(i,j)
                if test:
                    testUnknowns = self.unknownCheck((testY,testX))
                    if testUnknowns > maxUnknowns:
                        maxUnknowns = testUnknowns
                        bestmove = [(testY, testX)]
                    elif testUnknowns == maxUnknowns:
                        bestmove.append((testY, testX))
        if bestmove is []:
            nearestUnknown = self.moveTo(self.findUnknown())
            if nearestUnknown is None:
                return self.nanoRand()
            return nearestUnknown
        else:
           # if len(bestmove) > 1:
           return self.moveTo(self.findUnknown())
           # return random.choice(bestmove)
        
    def findUnknown(self):
        minDist = self.board.size
        nearestUnknown = None
        dist = 1
        while nearestUnknown is None:
            for i in range(-1*dist, dist):
                for j in range(-1*dist, dist):
                    if self.board.size > self.loc[0]+i and self.board.size > self.loc[1]+j and self.loc[1]+j>0 and self.loc[0]+i>0:
                        if not self.board.gridrepr[i+self.loc[0]][j+self.loc[1]].viewed:
                            nearestUnknown = (i+self.loc[0], j+self.loc[1])
            dist = dist+1
        return nearestUnknown

            
            
            
            
            